package com.ops.uitl.data;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

public class DatasetList extends ArrayList implements IDataset {

	private String serializableId;
	private String serializablePath;
	private boolean serializable;
	private boolean batchSerializable;
	private int batchPageSize;
	private int batchPageCount;
	private Map transactKeys;
	private int count = -1;
	
	/**
	 * construct function
	 */
	public DatasetList() {
		super();
	}
	
	/**
	 * construct function
	 * @param list
	 */
	public DatasetList(List list) {
		addAll(list);
	}
	
	/**
	 * construct function
	 * @param value
	 */
	public DatasetList(String value) {
		List list = DataHelper.strToList(value);
		if (list != null) addAll(list);
	}
	
	/**
	 * get serializable id
	 * @return String
	 */
	public String getSerializableId() {
		return serializableId;
	}
	
	/**
	 * set serializable id
	 * @param serializableId
	 */
	public void setSerializableId(String serializableId) {
		this.serializableId = serializableId;
	}
	
	/**
	 * get serializable path
	 * @return String
	 */
	public String getSerializablePath() {
		return serializablePath;
	}
	
	/**
	 * set serializable path
	 * @param serializablePath
	 */
	public void setSerializablePath(String serializablePath) {
		this.serializablePath = serializablePath;
	}
	
	/**
	 * is serializable
	 * @return boolean
	 */
	public boolean isSerializable() {
		return serializable;
	}

	/**
	 * set serializable
	 * @param serializable
	 */
	public void setSerializable(boolean serializable) {
		this.serializable = serializable;
	}
	
	/**
	 * is batch serializable
	 * @return boolean
	 */
	public boolean isBatchSerializable() {
		return batchSerializable;
	}

	/**
	 * set batch serializable
	 * @param batchSerializable
	 */
	public void setBatchSerializable(boolean batchSerializable) {
		this.batchSerializable = batchSerializable;
	}
	
	/**
	 * set batch page size
	 * @return int
	 */
	public int getBatchPageSize() {
		return batchPageSize;
	}
	
	/**
	 * set batch page size
	 * @param batchPageSize
	 */
	public void setBatchPageSize(int batchPageSize) {
		this.batchPageSize = batchPageSize;
	}

	/**
	 * set batch page count
	 * @return int
	 */
	public int getBatchPageCount() {
		return batchPageCount;
	}
	
	/**
	 * set batch page count
	 * @param batchPageSize
	 */
	public void setBatchPageCount(int batchPageCount) {
		this.batchPageCount = batchPageCount;
	}
	
	/**
	 * get transact keys
	 * @deprecated deprecated,temporarily method.
	 * @return Map
	 * @throws Exception
	 */
	public Map getTransactKeys() throws Exception {
		return transactKeys;
	}
	
	/**
	 * set transact keys
	 * @param transactKeys
	 * @deprecated deprecated,temporarily method.
	 * @throws Exception
	 */
	public void setTransactKeys(Map transactKeys) throws Exception {
		this.transactKeys = transactKeys;
	}
	
	/**
	 * get object
	 * @param index
	 * @return Object
	 */
	public Object get(int index) {
		Object value = super.get(index);
		if (value == null) return null;
		
		if (value instanceof JSONObject) {
			IData data = new DataMap();
			data.putAll((JSONObject) value);
			value = data;
		} else if (value instanceof JSONArray) {
			IDataset dataset = new DatasetList();
			dataset.addAll((JSONArray) value);
			value = dataset;
		}
		
		this.set(index, value);
		
		return value;
	}
	
	/**
	 * get object
	 * @param index
	 * @param name
	 * @return Object
	 */
	public Object get(int index, String name) {
		IData data = (IData) get(index);
		return data == null ? null : data.get(name);
	}
	
	/**
	 * get object
	 * @param index
	 * @param name
	 * @param def
	 * @return Object
	 */
	public Object get(int index, String name, Object def) {
		Object value = get(index, name);
		return value == null ? def : value;
	}
	
	/**
	 * get data
	 * @param index
	 * @return IData
	 */
	public IData getData(int index) throws Exception {
		return (IData) get(index);
	}
		
	/**
	 * get names
	 * @return String[]
	 */
	public String[] getNames() {
		return size() > 0 ? ((IData) get(0)).getNames() : null;
	}
	
	/**
	 * to data
	 * @return IData
	 */
	public IData toData() throws Exception {
		IData data = new DataMap();
		
		Iterator it = iterator();
		while (it.hasNext()) {
			IData element = (IData) it.next();
			Iterator iterator = element.keySet().iterator();
			while (iterator.hasNext()) {
				String key = (String) iterator.next();
				if (data.containsKey(key)) {
					IDataset list = (IDataset) data.get(key);
					list.add(element.get(key));
				} else {
					IDataset list = new DatasetList();
					list.add(element.get(key));
					data.put(key, list);
				}
			}			
		}
		
		if ("".equals(data.get("X_RECORDNUM", ""))) data.put("X_RECORDNUM", String.valueOf(size()));
		
		return data;
	}
	
	
	/**
	 * filter
	 * for example:filter="KEY1=VALUE1,KEY2=VALUE2"
	 * @param filter
	 * @return IDataset
	 * @throws Exception
	 */
	public IDataset filter(String filter) throws Exception {
		if (filter == null || "".equals(filter)) return this;
		
		IData ftdt = new DataMap();
		String[] fts = filter.split(",");
		for (int i=0; i<fts.length; i++) {
			String[] ft = fts[i].split("=");
			ftdt.put(ft[0], ft[1]);
		}
		
		IDataset subset = new DatasetList();
		for (int i=0; i<size(); i++) {
			IData subdata = (IData) get(i);
			boolean include = true;
			String[] ftdtNames = ftdt.getNames();
			for (int j=0; j<ftdtNames.length; j++) {
				String subvalue = (String) subdata.get(ftdtNames[j]);
				if (subvalue == null || !subvalue.equals(ftdt.get(ftdtNames[j]))) {
					include = false;
					break;
				}
			}
			if (include) subset.add(subdata);
		}
		
		return subset;
	}
	
	/**
	 * distinct
	 * @param fieldNames
	 * @param token
	 * @return
	 * @throws Exception
	 */
	public IDataset distinct(String fieldNames, String token) throws Exception {
		if ("".equals(fieldNames)) return this;
		
		List fieldValues = new ArrayList();
		IDataset subset = new DatasetList();
		String theToken = token == null || "".equals(token) ? "," : token;
		
		String[] keys = fieldNames.split(theToken);
		for (int i=0; i<size(); i++) {
			String fieldValue = "";
			for (int j = 0; j < keys.length; j++) {
				fieldValue += (String) get(i, keys[j]) + theToken;
			}
			if ("".equals(fieldValue)) continue;
			if (!fieldValues.contains(fieldValue)) {
				fieldValues.add(fieldValue);
				subset.add(get(i));
			}
		}
		return subset;
	}

	
	/**
	 * distinct
	 * @param fieldName
	 * @return IDataset
	 * @throws Exception
	 */
	public IDataset distinct(String fieldNames) throws Exception {
		return distinct(fieldNames, ",");
	}
	/**
	 * get count
	 * @return int
	 */
	public int count() {
		return count == -1 ? size() : count;
	}
	
	/**
	 * set count
	 * @param count
	 */
	public void setCount(int count) {
		this.count = count;
	}

/**
	 * sort single (default ascend)
	 */
	public void sort(String key, int keyType) {
		sort(key, keyType, IDataset.ORDER_ASCEND);
	}

	/**
	 * sort single
	 */
	public void sort(String key, int keyType, int order) {
		Object[] datas = (Object[]) this.toArray();
		DataComparator c = new DataComparator(key, keyType, order);
		Arrays.sort(datas, c);

		List list = Arrays.asList(datas);

		this.clear();
		this.addAll(list);
	}

	/**
	 * sort double (default ascend)
	 */
	public void sort(String key1, int keyType1, String key2, int keyType2) {
		sort(key1, keyType1, IDataset.ORDER_ASCEND);
		sort(key2, keyType2, IDataset.ORDER_ASCEND, key1, keyType1);
	}

	/**
	 * sort double
	 */
	public void sort(String key1, int keyType1, int order1, String key2, int keyType2, int order2) {
		sort(key1, keyType1, order1);
		sort(key2, keyType2, order2, key1, keyType1);
	}

	/**
	 * sort assistant
	 */
	private void sort(String key, int type, int order, String fix, int fixType) {
		Object[] datas = (Object[]) this.toArray();

		DataComparator c = new DataComparator(key, type, order);

		if (fix == null) {
			Arrays.sort(datas, c);
		} else {
			int[] marks = Anchor.mark(this, fix, fixType);

			for (int pre = 0, i = 1; i < marks.length; i++) {
				Arrays.sort(datas, pre, marks[i], c);
				pre = marks[i];
			}
		}
		List list = Arrays.asList(datas);

		this.clear();
		this.addAll(list);
	}
	
	/**
	 * to string
	 * @return String
	 */
    public String toString() {
    	StringBuffer str = new StringBuffer();
    	str.append("[");
    	
    	Iterator it = iterator();
    	while (it.hasNext()) {
    		Object value = it.next();
    		if (value == null) {
    			str.append("\"\"");
      		} else if (value instanceof Map || value instanceof List) {
    			str.append(value);
    		} else {
    			str.append("\"" + value + "\"");
    		}
    		if (it.hasNext()) str.append(",");
    	}
    	
    	str.append("]");
    	return str.toString();
	}
    
	/**
	 * to wade string
	 * @return String
	 */
    public String toWadeString() {
    	StringBuffer str = new StringBuffer();
    	str.append("[");
    	
    	Iterator it = iterator();
    	while (it.hasNext()) {
    		Object value = it.next();
    		if (value == null) {
    			str.append("\"\"");
    		} else if (value instanceof JSONObject) {
    			IData object = new DataMap();
    			object.putAll((Map) value);
    			str.append(object.toWadeString());
      		} else if (value instanceof JSONArray) {
    			IDataset object = new DatasetList();
    			object.addAll((List) value);
    			str.append(object.toWadeString());
    		} else if (value instanceof Map) {
    			str.append(((IData) value).toWadeString());
    		} else if (value instanceof List) {
    			str.append(((IDataset) value).toWadeString());
    		} else {
    			str.append("\"" + value + "\"");
    		}
    		if (it.hasNext()) str.append(", ");
    	}
    	
    	str.append("]");
    	return str.toString();
	}
    
	/**
	 * to bude string
	 * @return String
	 */
    public String toBudeString() {
    	StringBuffer str = new StringBuffer();
    	str.append("[");
    	
    	Iterator it = iterator();
    	while (it.hasNext()) {
    		Object value = it.next();
    		if (value == null) {
    			str.append("\"\"");
    		} else if (value instanceof JSONObject) {
    			IData object = new DataMap();
    			object.putAll((Map) value);
    			str.append(object.toBudeString());
      		} else if (value instanceof JSONArray) {
    			IDataset object = new DatasetList();
    			object.addAll((List) value);
    			str.append(object.toBudeString());
    		} else if (value instanceof Map) {
    			str.append(((IData) value).toBudeString());
    		} else if (value instanceof List) {
    			str.append(((IDataset) value).toBudeString());
    		} else {
    			str.append("\"" + value.toString().replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\"") + "\"");
    		}
    		if (it.hasNext()) str.append(", ");
    	}
    	
    	str.append("]");
    	return str.toString();
	}
    
}